#include<iostream>
#include<string>
#include<vector>
#include<boost/filesystem.hpp>
#include"util.hpp"
 //是一个目录，下面放的是所有html网页
    const std::string src_path = "data/input";
    const std::string output = "data/raw_html/raw.xtxt";//写入到这个目录中
    //内容，标题，该文档在官方中的url
    typedef struct DocInfo
    {
        std::string title;//文档的标题
        std::string content;//内容
        std::string url;//该文档在官方中的url
    }DocInfo_t;
     //const &输出
    bool EnumFile(const std::string &src_path,std::vector<std::string>*files_list);
    //*输入
    bool ParseHtml(const std::vector<std::string> &files_list,std::vector<DocInfo_t> *results);
    //输入输出
    bool SavaHtml(const std::vector<DocInfo_t> &results,const std::string &output);
int main()
{
    //递归式的把每个html文件名带路径保存到files_list中
    std::vector<std::string > files_list;
    if(!EnumFile(src_path,&files_list))
        {
            std::cerr<<"enum file name error!"<<std::endl;
            return 1;
        }
    //按照files_list读取每个文件的呢欧容，并解析
    std::vector<DocInfo_t> results;
    if(!ParseHtml(files_list,&results))
    {
        std::cerr<<"parse html error"<<std::endl;
        return 2;
    }
    //解析完成的内容写入到output中 \3作为每个文档的分隔符
    if(!SavaHtml(results,output))
    {
        std::cerr<<"sava html error"<<std::endl;
        return 3;
    }
    return 0;
}
//枚举文件
bool EnumFile(const std::string &src_path,std::vector<std::string>*files_list)
{
    namespace fs = boost::filesystem;
    fs::path root_path(src_path);
    //判断路径是否存在，
    if(!fs::exists(root_path))
    {
        std::cerr<<src_path<<"not exists"<<std::endl;
        return false;
    }
    //定义一个迭代器 用来判断递归是否结束
    fs::recursive_directory_iterator end;
    for(fs::recursive_directory_iterator iter(root_path);iter!=end;iter++)
    {
        //判断文件是否是html文件
        if(!fs::is_character_file(*iter))
        {
            continue;
        }
        if(iter->path().extension()!=".html")
        {
            //判断文件路径名的后缀是否符合要求
            continue;
        }
        files_list->push_back(iter->path().string());//将所有带路径html保存在files_list中

    }
    return true;
}

//解析标题
static bool ParseTitle(const std::string &file,std::string *title)
{
   //<html><head><title>Hello World</title></head></html>
    std::size_t begin=file.find("<title>");//定义标题开始位置(下标)//12
    if(begin == std::string::npos)//判断是否为空
    {
        return false;
    }
    //<html><head><title>Hello World</title></head></html>
    std::size_t end = file.find("</title>");//定义标题末尾位置(下标)29
    if(end==std::string::npos)
    {
        return false;
    }
    //<title>efghijk</title>
    begin+=std::string ("<title>").size();//如：<html><head><title>Hello World</title></head></html> 原begin =12，更新新下表12+7=19 起始位置更改为这个父子末尾下一个字符下标
    if(begin>end)//判断当前下标是否大于end  19>30
    {
        return false;
    }
    *title = file.substr(begin,end-begin);//提取从19下标开始提取，提取长度为30-19 = 11 提取内容(Hello World)
    return true;
}
//解析内容
static bool ParseContent(const std::string file,std::string *content)//文件中获取内容
{
    //去标签，状态机
    enum status
    {
        LABLEN,//标签
        CONTENT//内容
    };
    enum status s = LABLEN;//定义一个枚举对象默认值为标签
    //<title>Hello World</title>
    for(char c:file)//文件内容一次给c进行判断标签还是内容//去标签
    {
        switch (s)//
        {
        case LABLEN:
            if(c=='>')//c==>说明是右标签在标签内开始记录>记录内容
            {
                s = CONTENT;//
            }
            break;
        case CONTENT:
        {
            if(c=='<')////新标签开始不记录遇到>开始记录 <title>
            {
                s=LABLEN;
            }
            else
            {
                if(c=='\n')//出现\n把字符\n改成空格
                {
                    c= ' ';
                }
                content->push_back(c);//字符写content中
            }
        }
            break;
        default://出现title跳出直到出现>
            break;
        }
    }
    return true;
}

//解析网站：
static bool ParseUrl(const std::string &file_path,std::string *url)
{
    std::string url_head = "https://www.boost.org/doc/libs/1_78_0/doc/html";//固定头
    std::string url_tail = file_path.substr(src_path.size());//从这个位置开始直到结尾//尾 data/input
    
    *url = url_head+url_tail;//
    return true;
}

//内容，标题 网站
static void ShowDoc(const DocInfo_t &doc)
{
    std::cout<<"title"<<doc.title<<std::endl;
    std::cout<<"content"<<doc.content<<std::endl;
    std::cout<<"url"<<doc.url<<std::endl;
}
//解析html
bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo_t> *results)
{
    for (const std::string &file : files_list)
    {
        // 读取文件
        std::string result;
        if (!ns_util::FileUtil::ReadFile(file, &result))
        {
            continue;
        }
        DocInfo_t doc;
        // 解析指定文件.提取title
        if (!ParseTitle(result, &doc.title))
        {
            continue;
        }
        // 解析文件。提取conent去标签
        if (!ParseContent(result, &doc.content))
        {
            continue;
        }
        // 解析指定文件路径，构建url
        if (ParseUrl(file, &doc.url))
        {
            continue;
        }
        // 完成了解析任务，当前文档的相关结果都保存doc里面
        results->push_back(std::move(doc));
        //ShowDoc(doc);//测试
    }
    return true;
}

//保存Html
bool SavaHtml(const std::vector<DocInfo_t> &results,const std::string &output)
{
#define SEP '\3'
    //按照二进制方式进行写入
    //
    std::ofstream out(output,std::ios::out | std::ios::binary);
    if(!out.is_open())
    {
        std::cerr<<"open"<<output<<"failed!"<<std::endl;
        return false;
    }

    for(auto &item:results)
    {
        std::string out_string;
        out_string=item.title;
        out_string+=SEP;
        out_string+=item.content;
        out_string+=SEP;
        out_string+=item.url;
        out_string+='\n';
        out.write(out_string.c_str(),out_string.size());

    }
    out.close();
    return true;
}